package com.ptp.backendcertms.business.service.impl;

import com.alibaba.excel.EasyExcel;
import com.ptp.backendcertms.api.external.dto.req.CertificateInfoReqDto;
import com.ptp.backendcertms.api.external.dto.req.ImportResultReqDto;
import com.ptp.backendcertms.api.external.dto.req.ReportInfoReqDto;
import com.ptp.backendcertms.api.external.dto.res.ImportFailDetailResDto;
import com.ptp.backendcertms.api.external.dto.res.ImportResultResDto;
import com.ptp.backendcertms.api.internal.constant.BusinessTypeEnum;
import com.ptp.backendcertms.api.internal.constant.SystemResponseEnum;
import com.ptp.backendcertms.api.internal.constant.cert.CertificateStatusEnum;
import com.ptp.backendcertms.api.internal.exception.SystemException;
import com.ptp.backendcertms.basiccompnents.data.dao.entity.CertificatePo;
import com.ptp.backendcertms.basiccompnents.data.dao.entity.ImportFaiDetailPo;
import com.ptp.backendcertms.basiccompnents.data.dao.entity.ImportRecordPo;
import com.ptp.backendcertms.basiccompnents.data.dao.entity.ReportPo;
import com.ptp.backendcertms.basiccompnents.data.dao.service.ICertificateService;
import com.ptp.backendcertms.basiccompnents.data.dao.service.IImportFailDetailService;
import com.ptp.backendcertms.basiccompnents.data.dao.service.IImportRecordService;
import com.ptp.backendcertms.basiccompnents.data.dao.service.IReportService;
import com.ptp.backendcertms.basiccompnents.properties.ProjectProperties;
import com.ptp.backendcertms.business.handler.CmsSnowFlake;
import com.ptp.backendcertms.business.listener.CertificateReadListener;
import com.ptp.backendcertms.business.listener.ReportReadListener;
import com.ptp.backendcertms.business.service.IFileBusinessService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.io.UrlResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 文件业务 服务实现
 *
 * @author songcongcong
 * @date 2025/5/3
 */
@Slf4j
@Service
public class FileBusinessServiceImpl implements IFileBusinessService {
    @Resource
    private CertificateReadListener certificateReadListener;
    @Resource
    private ReportReadListener reportReadListener;
    @Resource
    private ProjectProperties projectProperties;
    @Resource
    private CmsSnowFlake snowflake;
    @Resource
    private ICertificateService certificateService;
    @Resource
    private IReportService reportService;
    @Resource
    private IImportFailDetailService importFailDetailService;
    @Resource
    private IImportRecordService importRecordService;

    @Override
    public String importCertInfo(MultipartFile file) {
        List<CertificateInfoReqDto> certInfoDtoList = readCertificateContent(file);
        checkExcelListSize(certInfoDtoList);
        String batchNo = snowflake.nextIdStr();
        int successNum = 0;
        int failNum = 0;
        String fileName = file.getOriginalFilename();
        if (!CollectionUtils.isEmpty(certInfoDtoList)) {
            for (int i = 0; i < certInfoDtoList.size(); i++) {
                boolean result = saveCertificate(batchNo, i + 1, fileName, certInfoDtoList.get(i));
                if (result) {
                    successNum ++;
                } else {
                    failNum ++;
                }
            }
        }
        saveImportRecord(batchNo, BusinessTypeEnum.certificate, fileName, successNum, failNum);
        return batchNo;
    }

    @Override
    public String importReportInfo(MultipartFile file) {
        List<ReportInfoReqDto> reportInfoDtoList = readReportContent(file);
        checkExcelListSize(reportInfoDtoList);
        String batchNo = snowflake.nextIdStr();
        int successNum = 0;
        int failNum = 0;
        String fileName = file.getOriginalFilename();
        if (!CollectionUtils.isEmpty(reportInfoDtoList)) {
            for (int i = 0; i < reportInfoDtoList.size(); i++) {
                boolean result = saveReport(batchNo, i + 1, fileName, reportInfoDtoList.get(i));
                if (result) {
                    successNum ++;
                } else {
                    failNum ++;
                }
            }
        }
        saveImportRecord(batchNo, BusinessTypeEnum.report, fileName, successNum, failNum);
        return batchNo;
    }

    @Override
    public ImportResultResDto getImportResult(ImportResultReqDto reqDto) {
        String batchNo = reqDto.getBatchNo();
        ImportRecordPo importRecordPo = importRecordService.getByBatchNo(batchNo);
        ImportResultResDto resDto = new ImportResultResDto();
        resDto.setBatchNo(importRecordPo.getBatchNo());
        resDto.setFileName(importRecordPo.getFileName());
        resDto.setSuccessNum(importRecordPo.getSuccessNum());
        resDto.setFailNum(importRecordPo.getFailNum());
        if (resDto.getFailNum() == 0) {
            return resDto;
        }
        List<ImportFaiDetailPo> failPoList = importFailDetailService.getListByBatchNo(batchNo);
        List<ImportFailDetailResDto> failDtoList = failPoList.stream().map(po -> {
            ImportFailDetailResDto dto = new ImportFailDetailResDto();
            dto.setRowNo(po.getRowNo());
            dto.setBusinessNo(po.getBusinessNo());
            dto.setFailReason(po.getFailReason());
            return dto;
        }).collect(Collectors.toList());
        resDto.setFailList(failDtoList);
        return resDto;
    }

    @Override
    public ResponseEntity<org.springframework.core.io.Resource> downloadCertTemplate() {
        return getFileDownloadResponse(projectProperties.getTemplate().getCertificate());
    }

    @Override
    public ResponseEntity<org.springframework.core.io.Resource> downloadReportTemplate() {
        return getFileDownloadResponse(projectProperties.getTemplate().getReport());
    }

    /**
     * 读取证书Excel内容
     *
     * @param file 文件
     * @return 内容列表
     */
    private List<CertificateInfoReqDto> readCertificateContent(MultipartFile file) {
        try (InputStream inputStream = file.getInputStream()) {
            return EasyExcel.read(inputStream, CertificateInfoReqDto.class, certificateReadListener).sheet().doReadSync();
        } catch (Exception e) {
            log.error("读取证书信息失败:{}", e.getMessage(), e);
            throw new SystemException(SystemResponseEnum.IMPORT_DATA_FAIL.name(), SystemResponseEnum.IMPORT_DATA_FAIL.getMessage() + "，文件格式可能有误");
        }
    }

    /**
     * 读取报告Excel内容
     *
     * @param file 文件
     * @return 内容列表
     */
    private List<ReportInfoReqDto> readReportContent(MultipartFile file) {
        try (InputStream inputStream = file.getInputStream()) {
            return EasyExcel.read(inputStream, ReportInfoReqDto.class, reportReadListener).sheet().doReadSync();
        } catch (Exception e) {
            log.error("读取报告信息失败:{}", e.getMessage(), e);
            throw new SystemException(SystemResponseEnum.IMPORT_DATA_FAIL.name(), SystemResponseEnum.IMPORT_DATA_FAIL.getMessage() + "，文件格式可能有误");
        }
    }

    /**
     * 检查excel数据量
     */
    private void checkExcelListSize(List<?> list) {
        if (CollectionUtils.isEmpty(list)) {
            throw new SystemException(SystemResponseEnum.IMPORT_DATA_FAIL.name(), SystemResponseEnum.IMPORT_DATA_FAIL.getMessage() + "：文件中无数据");
        }
        if (list.size() > projectProperties.getTemplate().getExcelListMaxSize()) {
            throw new SystemException(SystemResponseEnum.IMPORT_DATA_FAIL.name(), SystemResponseEnum.IMPORT_DATA_FAIL.getMessage() + "：文件数据量超出限制，最多" + projectProperties.getTemplate().getExcelListMaxSize() + "条");
        }
    }

    /***
     * 保存证书
     *
     * @param batchNo 导入批次号
     * @param rowNo 行号
     * @param fileName 文件名
     * @param dto 请求DTO
     * @return 保存结果，true-成功，false-失败
     */
    private boolean saveCertificate(String batchNo, int rowNo, String fileName, CertificateInfoReqDto dto) {
        // 校验参数
        String checkParamsResult = dto.checkParams();
        if (StringUtils.isNotBlank(checkParamsResult)) {
            saveImportFailDetail(batchNo, rowNo, BusinessTypeEnum.certificate, fileName, dto.getCertificateNo(), checkParamsResult);
            return false;
        }
        CertificatePo certificatePo = new CertificatePo();
        // 导入批次号
        certificatePo.setBatchNo(batchNo);
        // 流水号
        certificatePo.setSerialNo(snowflake.nextIdStr());
        // 删除标识，true-未删除，false-已删除
        certificatePo.setDelFlag(true);
        // 以下为必填参数
        // 组织名称
        certificatePo.setOrganizationName(dto.getOrganizationName());
        // 认证项目
        certificatePo.setCertificationProject(dto.getCertificationProject());
        // 证书编号
        certificatePo.setCertificateNo(dto.getCertificateNo());
        // 证书状态
        certificatePo.setCertificateStatus(CertificateStatusEnum.getByValue(dto.getCertificateStatus()));
        // 证书到期日期
        certificatePo.setDueDate(dto.getDueDate());
        // 以下为非必填参数
        // 统一社会信用代码
        certificatePo.setSocialCreditCode(dto.getSocialCreditCode());
        // 所在国别地区
        certificatePo.setArea(dto.getArea());
        // 本证书覆盖人数
        certificatePo.setCoverPeopleNumber(dto.getCoverPeopleNumber());
        // 组织地址
        certificatePo.setOrganizationAddress(dto.getOrganizationAddress());
        // 颁证日期
        certificatePo.setIssueDate(dto.getIssueDate());
        // 初次获证日期
        certificatePo.setInitialDate(dto.getInitialDate());
        // 监督次数
        certificatePo.setSuperviseTimes(dto.getSuperviseTimes());
        // 认证项目认证依据
        certificatePo.setCertificationBasis(dto.getCertificationBasis());
        // 认证覆盖的业务范围
        certificatePo.setBusinessScope(dto.getBusinessScope());
        // 是否覆盖多场所
        if (StringUtils.isNotBlank(dto.getIsMultipleLocations())) {
            certificatePo.setIsMultipleLocations("是".equals(dto.getIsMultipleLocations()));
        }
        // 认证覆盖的场所名称及地址
        certificatePo.setMultipleLocationsAddress(dto.getMultipleLocationsAddress());
        // 证书使用的认可标识
        certificatePo.setRecognitionMark(dto.getRecognitionMark());
        // 再次认证次数
        certificatePo.setRecertificationTimes(dto.getRecertificationTimes());
        try {
            certificateService.saveOrUpdateCertificateInfo(certificatePo);
            return true;
        } catch (Exception e) {
            log.error("certificateNo:{},保存证书信息失败:{}", dto.getCertificateNo(), e.getMessage(), e);
            saveImportFailDetail(batchNo, rowNo, BusinessTypeEnum.certificate, fileName, dto.getCertificateNo(), "数据库保存异常");
            return false;
        }
    }

    /***
     * 保存报告
     *
     * @param batchNo 导入批次号
     * @param fileName 文件名
     * @param dto 请求DTO
     * @return 保存结果，true-成功，false-失败
     */
    private boolean saveReport(String batchNo, int rowNo, String fileName, ReportInfoReqDto dto) {
        // 校验参数
        String checkParamsResult = dto.checkParams();
        if (StringUtils.isNotBlank(checkParamsResult)) {
            saveImportFailDetail(batchNo, rowNo, BusinessTypeEnum.report, fileName, dto.getReportNo(), checkParamsResult);
            return false;
        }
        ReportPo reportPo = new ReportPo();
        // 导入批次号
        reportPo.setBatchNo(batchNo);
        // 流水号
        reportPo.setSerialNo(snowflake.nextIdStr());
        // 删除标识
        reportPo.setDelFlag(true);
        // 以下为必填参数
        // 报告编号
        reportPo.setReportNo(dto.getReportNo());
        // 组织名称
        reportPo.setOrganizationName(dto.getOrganizationName());
        // 报告名称
        reportPo.setReportName(dto.getReportName());
        // 发布时间
        reportPo.setReleaseTime(dto.getReleaseTime());
        // 以下为非必填参数
        // 统一社会信用代码
        reportPo.setSocialCreditCode(dto.getSocialCreditCode());
        try {
            reportService.saveOrUpdateReportInfo(reportPo);
            return true;
        } catch (Exception e) {
            log.error("reportNo:{},保存报告信息失败:{}", dto.getReportNo(), e.getMessage(), e);
            saveImportFailDetail(batchNo, rowNo, BusinessTypeEnum.report, fileName, dto.getReportNo(), "数据库保存异常");
            return false;
        }
    }

    /**
     * 保存导入失败明细
     */
    private void saveImportFailDetail(String batchNo, int rowNo, BusinessTypeEnum businessType, String fileName, String businessNo, String failReason) {
        ImportFaiDetailPo importFaiDetailPo = new ImportFaiDetailPo();
        importFaiDetailPo.setBatchNo(batchNo);
        importFaiDetailPo.setRowNo(rowNo);
        importFaiDetailPo.setBusinessType(businessType);
        importFaiDetailPo.setFileName(fileName);
        importFaiDetailPo.setBusinessNo(businessNo);
        importFaiDetailPo.setFailReason(failReason);
        importFailDetailService.saveImportFailDetail(importFaiDetailPo);
    }

    /**
     * 保存导入记录
     */
    private void saveImportRecord(String batchNo, BusinessTypeEnum businessType, String fileName, int successNum, int failNum) {
        ImportRecordPo importRecordPo = new ImportRecordPo();
        importRecordPo.setBatchNo(batchNo);
        importRecordPo.setBusinessType(businessType);
        importRecordPo.setFileName(fileName);
        importRecordPo.setSuccessNum(successNum);
        importRecordPo.setFailNum(failNum);
        importRecordService.saveImportRecord(importRecordPo);
    }

    /**
     * 获取文件下载响应
     */
    private ResponseEntity<org.springframework.core.io.Resource> getFileDownloadResponse(String filePath) {
        org.springframework.core.io.Resource resource = null;
        try {
            // 文件路径
            Path path = Paths.get(filePath).toAbsolutePath().normalize();
            resource = new UrlResource(path.toUri());
            // 设置HTTP头信息
            return ResponseEntity.ok()
                    // 应用八位字节流，用于二进制数据传输，如文件下载
                    .contentType(MediaType.APPLICATION_OCTET_STREAM)
                    // 设置下载时文件名
                    .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getFilename() + "\"")
                    .body(resource);
        } catch (MalformedURLException e) {
            return ResponseEntity.internalServerError().build();
        } finally {
            try {
                if (Objects.nonNull(resource) && Objects.nonNull(resource.getInputStream())) {
                    resource.getInputStream().close();
                }
            } catch (IOException e) {
                log.error("模版下载关闭流异常:{}", e.getMessage(), e);
            }
        }
    }
}
